package com.example.spidertp.handler;

import com.example.spidertp.core.SpiderThreadPoolFactory;
import com.example.spidertp.core.mapper.TpWarningNoticeMapper;
import com.example.spidertp.core.model.entity.TpRejectedExecutionRecord;
import com.example.spidertp.core.model.entity.TpWarningNotice;
import com.example.spidertp.core.model.enums.TpWarningNoticeTypeEnum;
import com.example.spidertp.core.service.TpRejectedExecutionRecordService;
import com.example.spidertp.utils.DateUtils;
import com.example.spidertp.utils.SpringContextUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 对比发现，
 * ThreadPoolExecutor默认提供四种拒绝策略，
 * DiscardPolicy和DiscardOldestPolicy都会丢弃任务，并且没有记录可以找回，这在生产环境大多是不允许存在丢失任务的情况；
 * CallerRunsPolicy策略是由调用方的线程自行执行，当并发大的情况也或者调用方的线程已关闭，都会影响性能甚至是执行；
 * AbortPolicy策略,也是默认的策略，是以抛异常的形式拒绝执行任务，若没有处理异常可能会导致后续任务的执行，例如没有try住threadPool.execute(runnable);那么后续代码就停止了。
 * <p>
 * 因此可以自定义拒绝策略，或者多种策略，来满足自己业务的需求，这里仅是提供参考思路。
 * <p>
 * 此拒绝策略，是丢弃任务，但会记录
 *
 * @author xiong.bo
 * @version 1.0
 * @date 2022/9/25 12:01 下午
 */

@Slf4j
@Component
public class SpiderRejectedPolicy implements RejectedExecutionHandler {

    //间隔时间
    private static final Long INTERVAL = 5 * 60 * 1000L;

    //通知的时间
    private Long warningTime;


    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        log.info("拒绝策略记录start");
        TpRejectedExecutionRecord re = new TpRejectedExecutionRecord();
        String poolName = SpiderThreadPoolFactory.getPoolClassMap().get(String.valueOf(System.identityHashCode(executor)));
        re.setPoolName(poolName);
        re.setCorePoolSize(executor.getCorePoolSize());
        re.setMaximumPoolSize(executor.getMaximumPoolSize());
        re.setCreateTime(DateUtils.getCurrentTimestamp());
        re.setCreateUser("admin");//fix 还没有引入用户信息
        re.setPoolSize(executor.getPoolSize());
        re.setTaskCount(executor.getTaskCount());
        re.setActiveCount(executor.getActiveCount());
        re.setCompletedTaskCount(executor.getCompletedTaskCount());
        re.setLargestPoolSize(executor.getLargestPoolSize());
        TpRejectedExecutionRecordService executionRecordService = SpringContextUtil.getBean(TpRejectedExecutionRecordService.class);
        executionRecordService.save(re);
        log.info("拒绝策略记录end");

        //记录触发了拒绝策略后的警告通知
        saveWarningNotice(executor);

    }

    /**
     * 记录触发了拒绝策略后的警告通知
     * <p>
     * TpWarningNotice 和 TpRejectedExecutionRecord 的区别是：
     * 前者是有时间间隔，触发了条件会发送通知，目前只是保存在数据表中
     * 后者是每一次拒绝线程的执行都会记录
     *
     * @param executor
     */
    private void saveWarningNotice(ThreadPoolExecutor executor) {
        try {
            boolean timeFlag = warningTime == null || (System.currentTimeMillis() - warningTime) > INTERVAL;
            if (timeFlag) {
                TpWarningNotice wn = new TpWarningNotice();
                wn.setNoticeType(TpWarningNoticeTypeEnum.TRIGGER_REJECTION_POLICY_WARNING.getCode());
                String poolName = SpiderThreadPoolFactory.getPoolClassMap().get(String.valueOf(System.identityHashCode(executor)));
                wn.setPoolName(poolName);
                wn.setCorePoolSize(executor.getCorePoolSize());
                wn.setMaximumPoolSize(executor.getMaximumPoolSize());
                wn.setCurrentPoolSize(executor.getPoolSize());
                wn.setCurrentQueueSize(executor.getQueue().size());
                wn.setHisMaximumPoolSize(executor.getLargestPoolSize());
                wn.setKeepAliveTime(executor.getKeepAliveTime(TimeUnit.SECONDS));
                wn.setCreateUser("admin");
                wn.setCreateTime(DateUtils.getCurrentTimestamp());
                TpWarningNoticeMapper warningNoticeMapper = SpringContextUtil.getBean(TpWarningNoticeMapper.class);
                warningNoticeMapper.insert(wn);
                warningTime = System.currentTimeMillis();
            }
        } catch (Exception e) {
            log.info("SpiderRejectedPolicy.saveWarningNotice error!", e);
        }
    }
}
